



function setPixel(imageData, x, y, r, g, b, a) {
    index = (x + y * imageData.width) * 4;
    imageData.data[index+0] = r;
    imageData.data[index+1] = g;
    imageData.data[index+2] = b;
    imageData.data[index+3] = a;
}

var createCanvas = function (width, height){
	var buffer = document.createElement('canvas');
    buffer.width = width;
    buffer.height = height;
    return buffer;
};


var renderToCanvas = function (canvas, renderFunction) {
	var ctx = canvas.getContext('2d');
	ctx.clearRect ( 0 , 0 , canvas.width , canvas.height );
    renderFunction(ctx);
    return canvas;
};




function _metaball (_x, _y, _r, _behaviour) {
	this._x = (_x) ? _x : 0.0;
	this._y = (_y) ? _y : 0.0;
	this._r = (_r) ? _r : 0.0;
    this._r2 = (this._r * this._r);
	this._r2_100 = (this._r * this._r) * 1000;
	
	this._behaviour = (_behaviour) ? _behaviour : null;
	
	this.Equation = function(x,y){
	
		var dist2 = (x-this._x)*(x-this._x) + (y-this._y)*(y-this._y) ;
		
		if( dist2 < this._r2_100 )
        {
            //var dist = Math.sqrt( dist2 );
            
            return this._r2 / dist2;
                
            
            //return (this._r / dist );
        }
		else
			return 0;
	};
};
function metaball (_x, _y, _r, _behaviour){
	return new _metaball( _x, _y, _r, _behaviour );
};


function marchingSquare (_w,_h,_coef, _treshold,_Equation){
	return new _marchingSquare(_w,_h,_coef, _treshold,_Equation);
};

function _marchingSquare (_w,_h,_coef, _treshold,_Equation) {
	
	this._Equation = _Equation;
	
	this._treshold = _treshold;
	
	this._coef = _coef;
	this._w = _w / _coef;
	this._h = _h / _coef;
	this._off = _coef / 2;
	
	this.Data = [];
	this.Data.length = this._w*this._h;
	
	this.Data2 = [];
	this.Data2.length = (this._w-1)*(this._h-1);
	
	
	// function
	this.getData = function(_X,_Y){
		if( _X >= 0 && _X < this._w && _Y >=0 && _Y < this._h )
		{
			return this.Data[ _X + (_Y*this._w) ];
		}
		return 0;
	};
	this.setData = function(_X,_Y,_Number){
		if( _X >= 0 && _X < this._w && _Y >=0 && _Y < this._h )
		{
			this.Data[ _X + (_Y*this._w) ] = _Number;
		}
	};
	this.addData = function(_X,_Y,_Number){
		if( _X >= 0 && _X < this._w && _Y >=0 && _Y < this._h )
		{
			this.Data[ _X + (_Y*this._w) ] += _Number;
		}
	};
	
	this.getData2 = function(_X,_Y){
		if( _X >= 0 && _X < this._w-1 && _Y >=0 && _Y < this._h-1 )
		{
			return this.Data2[ _X + (_Y*(this._w-1)) ];
		}
		return 0;
	};
	this.setData2 = function(_X,_Y,_Number){
		if( _X >= 0 && _X < this._w-1 && _Y >=0 && _Y < this._h-1 )
		{
			this.Data2[ _X + (_Y*(this._w-1)) ] = _Number;
		}
	};
	this.addData2 = function(_X,_Y,_Number){
		if( _X >= 0 && _X < this._w-1 && _Y >=0 && _Y < this._h-1 )
		{
			this.Data2[ _X + (_Y*(this._w-1)) ] += _Number;
		}
	};
	
	
	this.clear = function(){
		for(var i=0; i<this.Data.length; i++)
			this.Data[i] = 0;
			
		for(var i=0; i<this.Data2.length; i++)
			this.Data2[i] = 0;
	};
	
	this.resolve = function(){
		this.clear();
		
		// set row data
		for(var x=0; x<this._w; x++){
			for(var y=0; y<this._h; y++){
				
				this.setData(x,y, this._Equation( x*this._coef + this._off, y*this._coef + this._off ) );
				
			}
		}
		
		// filter
		for(var i=0; i<this.Data.length; i++)
			this.Data[i] = this.Data[i] < this._treshold ? 0 : this.Data[i];
		
		
		// set Data 2
		for(var x=0; x<this._w-1; x++){
			for(var y=0; y<this._h-1; y++){
				
				if( this.getData(x,y) == 0 )
					this.addData2(x,y, 8);
					
				if( this.getData(x+1,y) == 0 )
					this.addData2(x,y, 4);
					
				if( this.getData(x+1,y+1) == 0 )
					this.addData2(x,y, 2);
					
				if( this.getData(x,y+1) == 0 )
					this.addData2(x,y, 1);
			}
		}
	};
	
	
	this.draw = function(ctx){
	
		for(var x=0; x<this._w-1; x++){
			for(var y=0; y<this._h-1; y++){
		
				var Vx = x*this._coef + this._off;
				var Vy = y*this._coef + this._off;
				var off = this._off;
				var coef = this._coef;
			
				switch( this.getData2(x,y) )
				{ // cc.drawingUtil.drawLine( cc.p(Vx,Vy), cc.p(Vx,Vy) );
				case 0: /*nothing*/ break;
				case 1: 
					cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy+coef) );
					break;
					
				case 2: 
					cc.drawingUtil.drawLine( cc.p(Vx+off,Vy+coef), cc.p(Vx+coef,Vy+off) );
					break;
					
				case 3: 
					cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+coef,Vy+off) );
					break;
					
				case 4: 
					cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+coef,Vy+off) );
					break;
					
				case 5: 
					cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy) );
					cc.drawingUtil.drawLine( cc.p(Vx+off,Vy+coef), cc.p(Vx+coef,Vy+off) );
					break;
				
				case 6: 
					cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+off,Vy+coef) );
					break;
					
				case 7: 
					cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy) );
					break;
					
				case 8: 
					cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy) );
					break;
					
				case 9: 
					cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+off,Vy+coef) );
					break;
				
				case 10: 
					cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+coef,Vy+off) );
					cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy+coef) );
					break;
					
				case 11: 
					cc.drawingUtil.drawLine( cc.p(Vx+off,Vy), cc.p(Vx+coef,Vy+off) );
					break;
					
				case 12: 
					cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+coef,Vy+off) );
					break;
					
				case 13: 
					cc.drawingUtil.drawLine( cc.p(Vx+off,Vy+coef), cc.p(Vx+coef,Vy+off) );
					break;
				
				case 14: 
					cc.drawingUtil.drawLine( cc.p(Vx,Vy+off), cc.p(Vx+off,Vy+coef) );
					break;
					
				case 15: 
					/* nothing */
					break;
				}
			}
		}
	};
};


var _metaworld = cc.Node.extend({
	_w:1,
	_h:1,
	
	_metaballs:[],
	_canvas:null,
	_IsoImage:null,
	
	// constante
	MIN_THRESHOLD:0.95,
	MAX_THRESHOLD:1.05,
	
	ctor:function (_W, _H) {
		this._super();
		this.scheduleUpdate();
		
		this._w = _W;
		this._h = _H;
	
		
		var __metaballs = this._metaballs;
		this.marching10 = marchingSquare(this._w,
										this._h,
										10, 
										this.MIN_THRESHOLD,
										function(X,Y){
											var sum = 0.0;
											for(var k=0;k<__metaballs.length;k++)
											{ 
												sum += __metaballs[k].Equation(X,Y); 
											}
											return sum;
										});
										
		this.marching5 = marchingSquare(this._w,
										this._h,
										5, 
										this.MIN_THRESHOLD,
										function(X,Y){
											var sum = 0.0;
											for(var k=0;k<__metaballs.length;k++)
											{ 
												sum += __metaballs[k].Equation(X,Y); 
											}
											return sum;
										});
										
										
		this.marching2 = marchingSquare(this._w,
										this._h,
										2, 
										this.MIN_THRESHOLD,
										function(X,Y){
											var sum = 0.0;
											for(var k=0;k<__metaballs.length;k++)
											{ 
												sum += __metaballs[k].Equation(X,Y); 
											}
											return sum;
										});
										
	},
	
	
	handleTouch:function(touch){
    },
	handleTouchMove:function(touchLocation){
    },
	
	
	addMetaball:function( _metaball ){
		this._metaballs.push( _metaball );
	},
	
	update:function(dt){
		
		for(var k=0;k<this._metaballs.length;k++)
		{ 
			if( this._metaballs[k]._behaviour != null )
			{
				this._metaballs[k]._behaviour(dt);
			}
		}
		
	},
	
	clearIsoImage:function(){
		
		for(var x=0;x<this._w;x++)
		{
			for(var y=0;y<this._h;y++)
			{
				setPixel(this._IsoImage, x, y, 0, 0, 0, 255);
			}
		}
	},
	
	draw:function (cocos_ctx) {
		
		if(this._canvas == null)
			this._canvas = createCanvas(this._w, this._h);
		
		var __this = this;
		
		
		var cached = renderToCanvas( this._canvas, function (ctx) {
			__this.internalDraw(ctx);
		});
		cocos_ctx.drawImage(this._canvas, 0, -this._h);
    },
	
	
	drawTest:[ "BASE", "Circle","Circle_Optim","GRAD","META_GRAD","META_GRAD_BORDER","MARCHING_10","MARCHING_5","MARCHING_2"],
	drawActualTest:0,
	
	internalDraw:function (ctx) {
		
		var renderMode = this.drawTest[ this.drawActualTest ];
		
		
		
		if( renderMode == "ISO" )
		{
			if(this._IsoImage == null)
				this._IsoImage = ctx.createImageData(this._w, this._h);

			this.clearIsoImage();
			this.drawISO(ctx);
			ctx.putImageData(this._IsoImage, 0, 0);
		}
		else if( renderMode == "MARCHING_10" )
		{
			this.marching10.resolve();
			this.marching10.draw(ctx);
		}
		else if( renderMode == "MARCHING_5" )
		{
			this.marching5.resolve();
			this.marching5.draw(ctx);
		}
		else if( renderMode == "MARCHING_2" )
		{
			this.marching2.resolve();
			this.marching2.draw(ctx);
		}
		else if( renderMode == "GRAD" )
		{
			this.drawGradient(ctx);
		}
		else if( renderMode == "META_GRAD" )
		{
			this.drawMetaGradient(ctx);
		}
		else if( renderMode == "META_GRAD_BORDER" )
		{
			this.drawMetaGradientBorder(ctx);
		}
		
		else if( renderMode == "ISO_optim" )
		{
			if(this._IsoImage == null)
				this._IsoImage = ctx.createImageData(this._w, this._h);

			this.clearIsoImage();
			this.drawISO_Optim(ctx);
			ctx.putImageData(this._IsoImage, 0, 0);
		}
		else if( renderMode == "Circle" )
		{
			this.drawCircle(ctx);
		}
		else if( renderMode == "Circle_Optim" )
		{
			this.drawCircle_Optim(ctx);
		}
		else if( renderMode == "BASE" )
		{
			this.drawNormalBall(ctx);
		}
    },
	
	drawNormalBall:function(ctx){
		
		for(var k=0;k<this._metaballs.length;k++)
		{ 
			cc.drawingUtil.drawCircle(cc.p(this._metaballs[k]._x,
											this._metaballs[k]._y), 
									this._metaballs[k]._r
									, 0, 20, false);
		}
	},
	
	drawGradient:function(ctx){
		
		
			
		for(var a=0;a<this._metaballs.length;a++)
		{
			var X = this._metaballs[a]._x;
			var Y = this._metaballs[a]._y;
			var R = this._metaballs[a]._r /** 2*/;
			
			
			var grad = ctx.createRadialGradient(X, this._h-Y, R, X, this._h-Y, 3*R);
			grad.addColorStop(0, 'rgba(' + 0 +',' + 255 + ',' + 0 + ',255)');
			grad.addColorStop(1, 'rgba(' + 0 +',' + 255 + ',' + 0 + ',0)');
		
		
			ctx.beginPath();
			ctx.fillStyle = grad;
			ctx.arc(X, this._h-Y, 3*R, 0, Math.PI*2);
			ctx.closePath();
			ctx.fill();
			
		}
		
		
	},
	
	drawMetaGradient:function(ctx){
		this.drawGradient(ctx);
		
		// metaballize
		
		var imageData = ctx.getImageData(0,0,this._w,this._h);
        var pix = imageData.data;
		for (var i = 0, n = pix.length; i <n; i += 4) {
			if(pix[i+3]< 230 ){
				pix[i+3]=0;
			}
			else {
				pix[i+3]=255;
			}
		}
		ctx.putImageData(imageData, 0, 0);    
	},
	
	drawMetaGradientBorder:function(ctx){
		
		this.drawGradient(ctx);
		// metaballize
		
		var imageData = ctx.getImageData(0,0,this._w,this._h);
        var pix = imageData.data;
		for (var i = 0, n = pix.length; i <n; i += 4) {
			if(pix[i+3]< 222 ){
				pix[i+3]=0;
			}
			else if(pix[i+3]< 245 ){
				pix[i+0]=20;
				pix[i+1]=100;
				pix[i+2]=20;
				pix[i+3]=255;
			}
			else {
				pix[i+3]=255;
			}
		}
		ctx.putImageData(imageData, 0, 0);    
	},
	
	drawISO:function( ctx ){
		for(var x=0;x<this._w;x++)
		{
			for(var y=0;y<this._h;y++)
			{
				var sum = 0.0;
				for(var k=0;k<this._metaballs.length;k++)
				{ 
					sum += this._metaballs[k].Equation(x,y); 
				}
			
				if( sum <= this.MAX_THRESHOLD && sum >= this.MIN_THRESHOLD )
				{
					setPixel(this._IsoImage, x, this._h-y, 255, 0, 0, 255);
					//cc.drawingUtil.drawCircle(cc.p(x,y),1,0, 6, false);
				}
			} 
		}
	},
	
	drawISO_Optim:function(ctx){
		
		
		
		for(var a=0;a<this._metaballs.length;a++)
		{
			var X = this._metaballs[a]._x - this._metaballs[a]._r*4;
			if(X<0) X=0;
			
			var Y = this._metaballs[a]._y - this._metaballs[a]._r*4;
			if(Y<0) Y=0;
			
			var Loop = this._metaballs[a]._r*8;
			
			for(var x=X; (x<this._w) && (x<(X+Loop));x++)
			{
				for(var y=Y; (y<this._h) && (y<(Y+Loop));y++)
				{
					var sum = 0.0;

					for(var k=0;k<this._metaballs.length;k++)
					{ 
						sum += this._metaballs[k].Equation(x,y); 
					}

					if( sum <= this.MAX_THRESHOLD && sum >= this.MIN_THRESHOLD )
					{
						setPixel(this._IsoImage, x, this._h-y, 255, 0, 0, 255);
						//cc.drawingUtil.drawCircle(cc.p(x,y),1,0, 6, false);
					}
				} 
			}
		}
	},
	
	
	drawCircle:function( ctx ){
		for(var x=0;x<this._w;x++)
		{
			for(var y=0;y<this._h;y++)
			{
				var sum = 0.0;
				for(var k=0;k<this._metaballs.length;k++)
				{ 
					sum += this._metaballs[k].Equation(x,y); 
				}
			
				if( sum <= this.MAX_THRESHOLD && sum >= this.MIN_THRESHOLD )
				{
					cc.drawingUtil.drawCircle(cc.p(x,y),1,0, 6, false);
				}
			} 
		}
	},
	
	drawCircle_Optim:function(ctx){
		
		for(var a=0;a<this._metaballs.length;a++)
		{
			var X = this._metaballs[a]._x - this._metaballs[a]._r*4;
			if(X<0) X=0;
			
			var Y = this._metaballs[a]._y - this._metaballs[a]._r*4;
			if(Y<0) Y=0;
			
			var Loop = this._metaballs[a]._r*8;
			
			for(var x=X; (x<this._w) && (x<(X+Loop));x++)
			{
				for(var y=Y; (y<this._h) && (y<(Y+Loop));y++)
				{
					var sum = 0.0;

					for(var k=0;k<this._metaballs.length;k++)
					{ 
						sum += this._metaballs[k].Equation(x,y); 
					}

					if( sum <= this.MAX_THRESHOLD && sum >= this.MIN_THRESHOLD )
					{
						cc.drawingUtil.drawCircle(cc.p(x,y),1,0, 6, false);
					}
				} 
			}
		}
	},
	

	
});
function metaworld (_W, _H){
	return new _metaworld(_W, _H);
};



var _metaScene = cc.Layer.extend({
   
   MouseX:0,
   MouseY:0,
   obj:null,
   
    init:function () {
        var selfPointer = this;
        //////////////////////////////
        // 1. super init first
        this._super();

		this.setTouchEnabled(true);
		this.setKeyboardEnabled(true);
		
		
		var size = cc.Director.getInstance().getWinSize();

		// background

        var background = cc.Sprite.create("res/LightedBackground.png");
        background.setPosition(cc.p(size.width / 2, size.height / 2));
        background.setVisible(true);
        background.setAnchorPoint(cc.p(0.5, 0.5));
        this.addChild(background, 0);

	
		//Menu (back button)
		var items = new Array();
		var p = cc.p( (-size.width/2) + 20 ,
					  (-size.height/2) + 30 );
		items[0] = createButton("Back");
		items[0].setPosition( p );
		items[0].setCallback(this,
            function () {
				myApp.GoToScene( new HelloWorldScene() );
            });
		var myMenu = cc.MenuCustom.create(items);
		this.addChild(myMenu);
		
		this.scheduleUpdate();
		
		
		
		
		//specific
		
		
		// test meta
		var Coef = 1;
		
		var world = metaworld(size.width/Coef,size.height/Coef);
		world.setScale(Coef);
		this.addChild(world);
		
		// center ball
		world.addMetaball( metaball (size.width/2,size.height/2, 20) );
		
		// user ball
		this.obj = { x:10, y:10};
		var obj = this.obj;
		world.addMetaball( metaball (0, 0, 10, function(dt){
			this._x = obj.x;
			this._y = obj.y;
		}) );
		
		
		
		// cpu ball
		world.addMetaball( metaball (0, 0, 10, function(dt){
			this._x += 10*dt;
			this._y += 20*dt;
		}) );
		
		
		
		
		
		//world (world button)
		
		var LabelTTF = cc.LabelTTF.create("Test 1", "Arial", 17);
		LabelTTF.setAnchorPoint(cc.p(0, 0.5));
		LabelTTF.setPosition(cc.p(500,30));
		this.addChild(LabelTTF);
		LabelTTF.setString( world.drawTest[ world.drawActualTest ] );
		
		
		var Number = cc.LabelTTF.create("Number", "Arial", 17);
		Number.setAnchorPoint(cc.p(0, 0.5));
		Number.setPosition(cc.p(500,100));
		this.addChild(Number);
		Number.setString( world._metaballs.length );
		
		
		items = new Array();
		// switch render
		p = cc.p( 0 , (-size.height/2) + 30 );
		items[0] = createButton("Switch");
		items[0].setPosition( p );
		items[0].setCallback(this,
            function () {
				world.drawActualTest++;
				world.drawActualTest = world.drawActualTest % world.drawTest.length;
				
				LabelTTF.setString( world.drawTest[ world.drawActualTest ] );
            });
			
		// push/pop
		p = cc.p( 0 , (-size.height/2) + 80 );
		items[1] = createButton("Push");
		items[1].setPosition( p );
		items[1].setCallback(this,
            function () {
				
				world.addMetaball( metaball ( cc.RANDOM_0_1()*500, cc.RANDOM_0_1()*400, cc.RANDOM_0_1()*20) );
				Number.setString( world._metaballs.length );
			});
			
		p = cc.p( 0 , (-size.height/2) + 130 );
		items[2] = createButton("Pop");
		items[2].setPosition( p );
		items[2].setCallback(this,
            function () {
				
				world._metaballs.pop();
				Number.setString( world._metaballs.length );
			});
			
		myMenu = cc.MenuCustom.create(items);
		this.addChild(myMenu);
		
		
        return true;
    },
	
	update:function (dt) {
    },
	
	// inputs 
	onTouchesBegan:function (pTouch, event) {
    },
    onTouchesMoved:function(pTouch,pEvent){
		if(pTouch[0])
		{
			var localTouch = this.convertTouchToNodeSpace(pTouch[0]);
			var worldTouch = this.convertToWorldSpace(localTouch);
			worldTouch = cc.Director.getInstance().convertToGL(worldTouch);
			this.MouseX = worldTouch.x;
			this.MouseY = worldTouch.y;
			
			this.obj.x = this.MouseX ;
			this.obj.y = this.MouseY ;
		}
	},
	onTouchesEnded:function (pTouch,pEvent){
    },
    onKeyUp:function(e){
    },
    onKeyDown:function(e){
	},
});


var metaScene = cc.Scene.extend({
    onEnter:function () {
        this._super();
        var layer = new _metaScene();
        layer.init();
        this.addChild(layer);
    }
});


